package org.reborn.engine.model.mesh

import org.reborn.math._
import org.reborn.utils.niobuffer.BufferUtil

import org.reborn.engine.enums.ArrayType
import org.reborn.engine.enums.FaceMode._

import java.nio.FloatBuffer
import java.nio.IntBuffer
 

class DrawableVertexData() extends VertexData{
  private var arrayType:Option[ArrayType.ArrayType] = None
  private var pPVertex = 0
  private var vertexCount = 0
  
  private var vertData:Option[FloatBuffer] = None
  
  def aType():ArrayType.ArrayType = arrayType.getOrElse(null)
  def partsPerVertex():Int = pPVertex
  def verts():Int = vertexCount
  def arraySize():Int = if(vertData.isDefined) vertData.get.capacity else 0
  
  def getDataArray() = if(vertData.isDefined) vertData.get.array else null
  
  def getDataBuffer() = vertData.getOrElse(null)
    
  def apply(position:Int):Float = vertData.get.get(position);
  
  def getVertexAt(vertIndex:Int):VectorF = {
    val startInd = vertIndex * pPVertex
    var partList:List[Float] = Nil
    for(i <- startInd until (startInd + pPVertex) ){ partList = vertData.get.get(i) :: partList }
    
    VectorF(partList.reverse)
  }
  
  def updateData(data:VertexData){
    //If we haven't set any data yet, or if the amount of data that is being updated is different
    //then we need to abandon the old direct buffer and create a new one
    if(vertData.isEmpty || this.arraySize != data.arraySize){
      val buffer = BufferUtil.createDirectFloatBuffer(data.arraySize)
      vertData = Some(buffer)
    }
    
    
    //TODO Need to check and make sure vertData is big enough to put data into
    val oBuffer = data.getDataBuffer
    oBuffer.position(0)
    vertData.get.position(0)
    vertData.get.put(oBuffer)

    arrayType = Some(data.aType)
    pPVertex = data.partsPerVertex
    vertexCount = data.verts
    
  }
}

class DrawableMesh() extends Mesh{
  private var arrayTypeMap:Map[ArrayType.ArrayType,DrawableVertexData] = Map()
  
  private var faceData:Option[IntBuffer] = None
  private var faceDataCount = 0
  private var faceMode:Option[FaceMode] = None
  
  private var vertsPerFace = 0
  
  def getAvailableArrayTypes():Iterable[ArrayType.ArrayType] = arrayTypeMap.keySet
  def hasType(arrayType:ArrayType.ArrayType):Boolean = arrayTypeMap.contains(arrayType)
  def apply(arrayType:ArrayType.ArrayType):DrawableVertexData = arrayTypeMap(arrayType);
  
  def getFacesArray():Array[Int] = if(faceData.isDefined) faceData.get.array else null
  def getFacesBuffer():IntBuffer = faceData.getOrElse(null)
  
  def getFaceMode():FaceMode = faceMode.getOrElse(null)
  
  def verts():Int = arrayTypeMap(ArrayType.Vertex).verts
  def faces():Int = faceDataCount / verticesPerFace
  def verticesPerFace():Int = vertsPerFace

  protected def getTranslatedVertexIndex(faceIndex:Int):Int = faceData.get.get(faceIndex);
  
  def updateData(mesh:Mesh){
    var newTypeMap:Map[ArrayType.ArrayType,DrawableVertexData] = Map()
    
    //Update Vertex Data
    for(aType <- mesh.getAvailableArrayTypes){
      val thisData = arrayTypeMap.get(aType);
      val oData = mesh(aType)
      
      val loopVertData = thisData.getOrElse(new DrawableVertexData())
      loopVertData.updateData(oData)
      
      newTypeMap += ((aType,loopVertData)) 
    }
    arrayTypeMap = newTypeMap
    
    //Update face data
    if(faceData.isEmpty || verticesPerFace*faces != mesh.verticesPerFace*mesh.faces){
      val buffer = BufferUtil.createDirectIntBuffer(mesh.faces * mesh.verticesPerFace)
      faceData = Some(buffer)
    }
    
    val oBuffer = mesh.getFacesBuffer
    oBuffer.position(0)
    faceData.get.position(0)
    faceData.get.put(oBuffer)
    
    vertsPerFace = mesh.verticesPerFace
    faceDataCount = faceData.get.capacity
    faceMode = Some(mesh.getFaceMode)
  }
  
  def updateFaceData(mesh:Mesh){
    //Update face data
    if(faceData.isEmpty || verticesPerFace*faces != mesh.verticesPerFace*mesh.faces){
      val buffer = BufferUtil.createDirectIntBuffer(mesh.faces * mesh.verticesPerFace)
      faceData = Some(buffer)
    }
    
    val oBuffer = mesh.getFacesBuffer
    oBuffer.position(0)
    faceData.get.position(0)
    faceData.get.put(oBuffer)
    
    vertsPerFace = mesh.verticesPerFace
    faceDataCount = faceData.get.capacity
    faceMode = Some(mesh.getFaceMode)
  }
}